use conn::SerialPortConn;
use proto::Frame;
use tokio::sync::RwLock;

use crate::error::AppErr;

mod proto;
mod conn;
mod dispatch;
mod notify;
pub use proto::address;
pub use proto::cmd;
pub use proto::BodyDecode;
pub use proto::BodyEncode;

pub fn available_ports() -> Result<Box<[String]>, AppErr> {
    let ports = tokio_serial::available_ports()?;
    let ps: Box<[String]> = ports.into_iter().map(|item| item.port_name).collect();
    Ok(ps)
}

static SERIAL_PORT: RwLock<Option<SerialPortConn>> = RwLock::const_new(None);

pub async fn open_port(name: &str, baud_rate: u32) -> Result<(), AppErr> {
    close_port().await;
    let conn = SerialPortConn::new(name, baud_rate)?;
    *SERIAL_PORT.write().await = Some(conn);
    Ok(())
}

pub async fn close_port() {
    let mut port = SERIAL_PORT.write().await;
    let port = port.take();
    match port {
        Some(port) => {
            port.close().await;
        },
        None => {
        }
    };
}

pub async fn port_is_open() -> bool {
    SERIAL_PORT.read().await.is_some()
}

pub async fn write_port(buf: &[u8]) -> Result<(), AppErr> {
    let port = SERIAL_PORT.read().await;
    match port.as_ref() {
        Some(port) => {
            port.write(buf).await?;
        },
        None => {
            return Err(AppErr::Static("串口没有打开"));
        }
    };
    Ok(())
}

pub async fn ping(dst: u8) -> Result<(), AppErr> {
    let port = SERIAL_PORT.read().await;
    let p2=  match port.as_ref() {
        Some(port) => {
            port
        },
        None => {
            return Err(AppErr::Static("串口没有打开"));
        }
    };
    p2.ping(dst).await?;
    Ok(())
}

pub async fn simple_req<T: BodyEncode>(dst: u8, cmd: u8, body: T) -> Result<Frame, AppErr> {
    let port = SERIAL_PORT.read().await;
    let p2=  match port.as_ref() {
        Some(port) => {
            port
        },
        None => {
            return Err(AppErr::Static("串口没有打开"));
        }
    };
    p2.simple_req(dst, cmd, body).await
}

pub async fn req<T: BodyEncode>(dst: u8, cmd: u8, body: T) -> Result<Frame, AppErr> {
    let port = SERIAL_PORT.read().await;
    let p2=  match port.as_ref() {
        Some(port) => {
            port
        },
        None => {
            return Err(AppErr::Static("串口没有打开"));
        }
    };
    p2.req(dst, cmd, body).await
}

